///PHILOTES Source Code.  (C)2012 PhiloLabs

#include "OgreStableHeaders.h"
#include "OgrePose.h"
#include "OgreHardwareBufferManager.h"

namespace Ogre {
	//---------------------------------------------------------------------
	Pose::Pose(ushort target, const String& name)
		: mTarget(target), mName(name)
	{
	}
	//---------------------------------------------------------------------
	Pose::~Pose()
	{
	}
	//---------------------------------------------------------------------
	void Pose::addVertex(size_t index, const Vector3& offset)
	{
		if (!mNormalsMap.empty())
			PHILO_EXCEPT(Exception::ERR_INVALIDPARAMS, 
				"Inconsistent calls to addVertex, must include normals always or never",
				"Pose::addVertex");

        if(offset.squaredLength() < 1e-6f)
        {
            return;
        }

        mVertexOffsetMap[index] = offset;
		mBuffer.setNull();
	}
	//---------------------------------------------------------------------
	void Pose::addVertex(size_t index, const Vector3& offset, const Vector3& normal)
	{
		if (!mVertexOffsetMap.empty() && mNormalsMap.empty())
			PHILO_EXCEPT(Exception::ERR_INVALIDPARAMS, 
				"Inconsistent calls to addVertex, must include normals always or never",
				"Pose::addVertex");

        if(offset.squaredLength() < 1e-6f && normal.squaredLength() < 1e-6f)
        {
            return;
        }

        mVertexOffsetMap[index] = offset;
		mNormalsMap[index] = normal;
		mBuffer.setNull();
	}
	//---------------------------------------------------------------------
	void Pose::removeVertex(size_t index)
	{
		VertexOffsetMap::iterator i = mVertexOffsetMap.find(index);
		if (i != mVertexOffsetMap.end())
		{
			mVertexOffsetMap.erase(i);
			mBuffer.setNull();
		}
		NormalsMap::iterator j = mNormalsMap.find(index);
		if (j != mNormalsMap.end())
		{
			mNormalsMap.erase(j);
		}
	}
	//---------------------------------------------------------------------
	void Pose::clearVertices(void)
	{
		mVertexOffsetMap.clear();
		mNormalsMap.clear();
		mBuffer.setNull();
	}
	//---------------------------------------------------------------------
	Pose::ConstVertexOffsetIterator 
		Pose::getVertexOffsetIterator(void) const
	{
		return ConstVertexOffsetIterator(mVertexOffsetMap.begin(), mVertexOffsetMap.end());
	}
	//---------------------------------------------------------------------
	Pose::VertexOffsetIterator 
		Pose::getVertexOffsetIterator(void)
	{
		return VertexOffsetIterator(mVertexOffsetMap.begin(), mVertexOffsetMap.end());
	}
	//---------------------------------------------------------------------
	Pose::ConstNormalsIterator Pose::getNormalsIterator(void) const
	{
		return ConstNormalsIterator(mNormalsMap.begin(), mNormalsMap.end());
	}
	//---------------------------------------------------------------------
	Pose::NormalsIterator Pose::getNormalsIterator(void)
	{
		return NormalsIterator(mNormalsMap.begin(), mNormalsMap.end());
	}
	//---------------------------------------------------------------------
	const HardwareVertexBufferSharedPtr& Pose::_getHardwareVertexBuffer(const VertexData* origData) const
	{
		size_t numVertices = origData->vertexCount;
		
		if (mBuffer.isNull())
		{
			// Create buffer
			size_t vertexSize = VertexElement::getTypeSize(VET_FLOAT3);
			bool normals = getIncludesNormals();
			if (normals)
				vertexSize += VertexElement::getTypeSize(VET_FLOAT3);
				
			mBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
				vertexSize,	numVertices, HardwareBuffer::HBU_STATIC_WRITE_ONLY);

			float* pFloat = static_cast<float*>(
				mBuffer->lock(HardwareBuffer::HBL_DISCARD));
			// initialise - these will be the values used where no pose vertex is included
			memset(pFloat, 0, mBuffer->getSizeInBytes()); 
			if (normals)
			{
				// zeroes are fine for positions (deltas), but for normals we need the original
				// mesh normals, since delta normals don't work (re-normalisation would
				// always result in a blended normal even with full pose applied)
				const VertexElement* origNormElem = 
					origData->vertexDeclaration->findElementBySemantic(VES_NORMAL, 0);
				assert(origNormElem);
				
				const HardwareVertexBufferSharedPtr& origBuffer = 
					origData->vertexBufferBinding->getBuffer(origNormElem->getSource());
				float* pDst = pFloat + 3;
				void* pSrcBase = origBuffer->lock(HardwareBuffer::HBL_READ_ONLY);
				float* pSrc;
				origNormElem->baseVertexPointerToElement(pSrcBase, &pSrc);
				for (size_t v = 0; v < numVertices; ++v)
				{
					memcpy(pDst, pSrc, sizeof(float)*3);
					
					pDst += 6;
					pSrc = (float*)(((char*)pSrc) + origBuffer->getVertexSize());
				}
				origBuffer->unlock();
				
				
			}
			// Set each vertex
			VertexOffsetMap::const_iterator v = mVertexOffsetMap.begin();
			NormalsMap::const_iterator n = mNormalsMap.begin();
			
			size_t numFloatsPerVertex = normals ? 6: 3;
			
			while(v != mVertexOffsetMap.end())
			{
				// Remember, vertex maps are *sparse* so may have missing entries
				// This is why we skip
				float* pDst = pFloat + (numFloatsPerVertex * v->first);
				*pDst++ = v->second.x;
				*pDst++ = v->second.y;
				*pDst++ = v->second.z;
				++v;
				if (normals)
				{
					*pDst++ = n->second.x;
					*pDst++ = n->second.y;
					*pDst++ = n->second.z;
					++n;
				}
				
			}
			mBuffer->unlock();
		}
		return mBuffer;
	}
	//---------------------------------------------------------------------
	Pose* Pose::clone(void) const
	{
		Pose* newPose = PHILO_NEW Pose(mTarget, mName);
		newPose->mVertexOffsetMap = mVertexOffsetMap;
        newPose->mNormalsMap = mNormalsMap;
		// Allow buffer to recreate itself, contents may change anyway
		return newPose;
	}

}

